﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Xsl;
using System.IO;
using System.Drawing;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;

namespace WordToSharePoint
{
    public class DocumentHandler
    {

        private List<string> logging = new List<string>();        
        private WordprocessingDocument parentDocument = null;
        private string webURL = string.Empty;
        private string documentName = string.Empty;
        private bool resizeImage = false;
        private SPFile documentFile = null;

        public DocumentHandler(SPFile file)
        {
            this.documentName = "i" + file.Name; // add character to differentiate transformed document                        
            documentFile = file;
        }

        public List<string> Process(string xsl, string webURL, string wikiFolder, string imageFolder, int imageMaxDimension, bool resizeImage)
        {            
            this.webURL = webURL;
            this.resizeImage = resizeImage;
            this.parentDocument = WordprocessingDocument.Open(documentFile.OpenBinaryStream(), true);

            try
            {
                logging.Add(string.Format("Document Processing Started: {0}", DateTime.Now.ToString()));

                // Load document and important related parts, like the styles
                XmlDocument mainDoc = new XmlDocument();
                mainDoc.Load(parentDocument.MainDocumentPart.GetStream());
                XmlNamespaceManager nsm = createNameSpaceManager(mainDoc.NameTable);
                XmlNode docNode = mainDoc.SelectSingleNode("./w:document", nsm);
                StyleDefinitionsPart styles = parentDocument.MainDocumentPart.StyleDefinitionsPart;
                if (styles != null)
                    LoadRelatedPart(mainDoc, docNode, styles.GetStream());
                NumberingDefinitionsPart numbering = parentDocument.MainDocumentPart.NumberingDefinitionsPart;
                if (numbering != null)
                    LoadRelatedPart(mainDoc, docNode, numbering.GetStream());
                ThemePart theme = parentDocument.MainDocumentPart.ThemePart;
                if (theme != null)
                    LoadRelatedPart(mainDoc, docNode, theme.GetStream());
                FontTablePart fontTable = parentDocument.MainDocumentPart.FontTablePart;
                if (fontTable != null)
                    LoadRelatedPart(mainDoc, docNode, fontTable.GetStream());

                HandleImages(mainDoc, nsm, parentDocument, webURL, imageFolder, imageMaxDimension);
                this.logging.Add("Image Processing Complete.");

                XslCompiledTransform OpenXmlTransformer = new XslCompiledTransform();
                XmlReader xslFile = XmlReader.Create(new StringReader(xsl));
                OpenXmlTransformer.Load(xslFile);

                // transform from XML to HTML
                StringWriter htmlStringWriter = new StringWriter();
                OpenXmlTransformer.Transform(mainDoc, null, htmlStringWriter);
                string transformedHTML = htmlStringWriter.ToString();
                CreateMOSSPage(wikiFolder, transformedHTML);
                this.logging.Add(string.Format("Document Transformation Complete. All Tasks Complete at {0}", DateTime.Now.ToString()));
            }
            catch (XsltCompileException)
            {
                throw new Exception("Invalid XSLT");
            }
            catch (XsltException ex)
            {
                this.logging.Add(string.Format("Error occured : {0}", ex.Message));
            }

            return this.logging;

        }

        private static void LoadRelatedPart(XmlDocument mainDoc, XmlNode node, Stream stream)
        {
            XmlDocument partDoc = new XmlDocument();
            partDoc.Load(stream);
            XmlNode partNode = mainDoc.ImportNode(partDoc.DocumentElement, true);
            if (partNode != null)
                node.AppendChild(partNode);
        }

        private static XmlNamespaceManager createNameSpaceManager(XmlNameTable nameTable)
        {
            XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(nameTable);
            nameSpaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
            nameSpaceManager.AddNamespace("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
            nameSpaceManager.AddNamespace("a", "http://schemas.openxmlformats.org/drawingml/2006/main");
            nameSpaceManager.AddNamespace("pic", "http://schemas.openxmlformats.org/drawingml/2006/picture");
            nameSpaceManager.AddNamespace("wp", "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing");
            nameSpaceManager.AddNamespace("v", "urn:schemas-microsoft-com:vml");
            nameSpaceManager.AddNamespace("w10", "urn:schemas-microsoft-com:office:word");
            nameSpaceManager.AddNamespace("o", "urn:schemas-microsoft-com:office:office");
            return nameSpaceManager;
        }

        private void HandleImages(XmlDocument mainDoc, XmlNamespaceManager nsm, OpenXmlPackage htmlPackage, string siteURL, string imageFolder, int imageMaxDimension)
        {
            XmlNodeList images = mainDoc.SelectNodes("//a:blip", nsm);
            foreach (XmlNode node in images)
            {
                string rid = GetAttributeValue(node, "r:embed");
                if (!String.IsNullOrEmpty(rid))
                {
                    string imageName = CreateMOSSImage(node, rid, siteURL, imageFolder, imageMaxDimension);
                    XmlAttribute imageAttr = mainDoc.CreateAttribute("imagePath");
                    imageAttr.Value = imageName;
                    node.Attributes.Append(imageAttr);
                }
            }

            images = mainDoc.SelectNodes("//v:imagedata", nsm);
            foreach (XmlNode node in images)
            {
                string rid = GetAttributeValue(node, "r:id");
                if (!String.IsNullOrEmpty(rid))
                {
                    string imageName = CreateMOSSImage(node, rid, siteURL, imageFolder, imageMaxDimension);
                    XmlAttribute imageAttr = mainDoc.CreateAttribute("imagePath");
                    imageAttr.Value = imageName;
                    node.Attributes.Append(imageAttr);
                }
            }
        }

        private string CreateMOSSImage(XmlNode node, string rId, string siteURL, string imageFolder, int imageMaxDimension)
        {
            string imageName = @"images\gray.bmp";            
            OpenXmlPart imagePartInput = parentDocument.MainDocumentPart.GetPartById(rId);
            string imagePath = imagePartInput.Uri.OriginalString;
            imageName = imagePath.Substring(imagePath.LastIndexOf('/') + 1);
            // Stores image part contents into buffer for later storing
            byte[] arBytes = new byte[imagePartInput.GetStream().Length];
            imagePartInput.GetStream().Read(arBytes, 0, arBytes.Length);
            Stream imageStream = this.resizeImage ? TransformImageStream(imagePartInput.GetStream(), imageMaxDimension) : imagePartInput.GetStream();
            string newFileName = SaveImageToMOSS(imageStream, imageName, imageFolder);
            return newFileName;
        }

        private Stream TransformImageStream(Stream str, double imageMaxDimAllowed)
        {
            MemoryStream newStream = new MemoryStream();

            try
            {
                // translate image as jpg and resize to max height/width ratio            
                Image image = Image.FromStream(str);
                double imageMaxDim = ((image.Height >= image.Width) ? image.Height : image.Width);
                double resizeRatio = ((imageMaxDimAllowed >= imageMaxDim) ? 1 : (imageMaxDimAllowed / imageMaxDim));
                int newWidth = (int)(image.Width * resizeRatio) + 1;
                int newHeight = (int)(image.Height * resizeRatio) + 1;
                Image resizedImage = new Bitmap(image, newWidth, newHeight);

                Graphics g = Graphics.FromImage(image);
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                g.DrawImage(resizedImage, newWidth, newHeight);

                resizedImage.Save(newStream, System.Drawing.Imaging.ImageFormat.Jpeg);
            }
            catch (Exception ex)
            {
                this.logging.Add(string.Format("ERROR: {0} :TRACE: {1}", ex.Message, ex.StackTrace));
            }
            return newStream;
        }

        private string SaveImageToMOSS(Stream str, string filename, string folderName)
        {
            SPSite site = new SPSite(this.webURL);
            SPWeb web = site.OpenWeb();

            try
            {
                SPFolder mylibrary = web.Folders[folderName];
                web.AllowUnsafeUpdates = true;
                string associateFileName = string.Format("{0}_{1}", this.documentName, filename);
                SPFile file = mylibrary.Files.Add(associateFileName, str, true);
                mylibrary.Update();
                string newFileName = string.Format(@"{0}/{1}", this.webURL, file.Url);
                this.logging.Add(string.Format("Successfully created {0}", newFileName));
                return newFileName;
            }
            catch (Exception ex)
            {
                this.logging.Add(string.Format("Error occured : {0}", ex.Message));
            }
            finally
            {
                web.Close();
                site.Close();
            }

            return string.Empty;
        }

        private void CreateMOSSPage(string folder, string pageContent)
        {

            SPSite site = new SPSite(this.webURL);
            SPWeb web = site.OpenWeb();

            try
            {
                SPFolder mylibrary = web.Folders[folder];
                web.AllowUnsafeUpdates = true;
                SPFile file = mylibrary.Files.Add(string.Format(@"{0}/{1}.aspx", mylibrary.ServerRelativeUrl, this.documentName), SPTemplateFileType.WikiPage);
                SPListItem item = file.Item;
                item["WikiField"] = pageContent;
                item.Update();
                this.logging.Add(string.Format("Successfully created {0}", file.Name));
            }
            catch (Exception ex)
            {
                this.logging.Add(string.Format("Error occured : {0}", ex.Message));
            }
            finally
            {
                web.Dispose();
                site.Dispose();
            }
        }

        internal static string GetAttributeValue(XmlNode node, string name)
        {
            string value = string.Empty;

            XmlAttribute attribute = node.Attributes[name];
            if (attribute != null && attribute.Value != null)
            {
                value = attribute.Value;
            }

            return value;
        }

    }
}
